package cn.itcast.gateway.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * 参考MyGatewayFilterFactory:在服务没启动时，给出友好提示~
 */
@Slf4j
@Component
public class DiscoveryGatewayFilterFactory extends AbstractGatewayFilterFactory<DiscoveryGatewayFilterFactory.Config> {
    /**
     * 不能少！
     */
    public DiscoveryGatewayFilterFactory() {
        super(Config.class);
    }


    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RouteDefinitionLocator routeDefinitionLocator;


    public Map<String, String> getPathServiceIdMap() {
        Flux<RouteDefinition> routeDefinitions = routeDefinitionLocator.getRouteDefinitions();
        Map<String, String> routeMap = new java.util.HashMap<>();
        routeDefinitions.subscribe(routeDefinition -> {
//            System.out.println("Route ID: " + routeDefinition.getId());
//            System.out.println("URI: " + routeDefinition.getUri());
//            System.out.println("Predicates: " + routeDefinition.getPredicates());
//            System.out.println("Filters: " + routeDefinition.getFilters());
//            System.out.println("-------------------------");

            routeDefinition.getPredicates().forEach(predicate -> predicate.getArgs().forEach((key, path) -> routeMap.put(path, routeDefinition.getId())));
        });

        return routeMap;
    }

    /**
     * 使用匿名内部类
     *
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                String discovery = config.getDiscovery();
                log.info("----- MyGatewayFilterFactory # status:{}", discovery);
                //{/order/**=order-service, /user/**=user-service, /gateway/**=gateway}
                if ("true".equals(discovery)) { //开关是否为true
                    Map<String, String> pathServiceIdMap = getPathServiceIdMap();
                    //请求路径：/user/login
                    String requestPath = exchange.getRequest().getPath().toString();
                    String serviceId = getServiceId(requestPath, pathServiceIdMap);
                    if (null == serviceId || serviceId.isEmpty()) {
                        log.info("--->>>requestPath:{} 没有对应的服务", requestPath);
                        exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                        return exchange.getResponse().setComplete();
                    }
                    //已经启动的服务列表
                    List<String> serviceIds = discoveryClient.getServices();
                    if (!serviceIds.contains(serviceId)) {
                        log.info("--->>>requestPath:{} 对应的服务id:{} 没有查找到", requestPath, serviceId);
                        exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                        return exchange.getResponse().setComplete();
                    }
                }
                return chain.filter(exchange);
            }
        };
    }


    /**
     * 获取服务id
     *
     * @param reqPath          请求路径：/user/login
     * @param pathServiceIdMap {/order/**=order-service, /user/**=user-service, /gateway/**=gateway}
     * @return String
     */
    private String getServiceId(String reqPath, Map<String, String> pathServiceIdMap) {
        Set<String> pathSet = pathServiceIdMap.keySet();
        for (String path : pathSet) {
            String prefix = path.replace("*", "");
            if (reqPath.contains(prefix)) {
                return pathServiceIdMap.get(path);
            }
        }
        return null;
    }


    /**
     * 此处必须和Config类中的字段名称"discovery"对应上~
     *
     * @return List<String>
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("discovery");
    }

    @Data
    public static class Config {
        private String discovery;
    }
}
